/*!
 * TrakkSocial 0.4
 * http://www.trakken.de/
 *
 * Copyright 2011, Trakken Web Services GmbH
 * Jörg Tillmann
 * Licensed under the MIT license.
 */
var TrakkSocial = (function(window, document, undefined) {



    /*
     * Debug mode outputs data to the console
     * Activate by either
     *      Adding ?trakkSocialDebug=1 to the URL
     *      Adding 'debug: true' to the options for the init method
     */
    var debug = false;



    /*
     * This function initializes the tracking, sets up the event binding
     * and is revealed to the outside as TrakkSocial.init()
     *
     * @param   options     Tracking options: {
     *                          embed:      Take care of embedding the necessary scripts
     *                          debug:      Enable debugging mode with console output

     *                          facebook:   Options handed to trakk_facebook method
     *                          twitter:    Options handed to trakk_twitter method
     *                          addthis:    True or false for triggering AddThis tracking
     *                          linkedin:   True or false for triggering LinkedIn tracking
     *                          xing:       True or false for triggering Xing tracking
     *                      }
     *                      Set any one to false to prevent tracking of that social service
     *                      If no options are supplied, everything is tracked, but
     *                      no scripts are embedded
     * @return  void
     */
    var init = function(options) {

        var options = options || {};

        var default_options = {

            facebook:       true,
            twitter:        true,
            linkedin:       false,
            xing:           false,
            custom:         false,

            embed:          false,
            debug:          false
        };

        for(option in default_options){
            if (default_options.hasOwnProperty(option)) {
                if (options[option] == undefined) {
                    options[option] = default_options[option];
                }
            }
        }

        if (extractParameter(window.location.href, 'trakkSocialDebug') === '1' || options.debug) {
            debug = true;
        }


        if (options.facebook) {
            trakk_facebook(options.facebook, options.embed);
        }

        if (options.twitter) {
            trakk_twitter(options.twitter, options.embed);
        }

        if (options.linkedin) {
            trakk_linkedin(options.linkedin, options.embed);
        }

        if (options.addthis || options.xing || options.custom) {
            trakk_anchors(options, options.embed);
        }

    };



    var trakk_anchors = function (options) {

        addLoadEvent(function () {

            if (window._gaq) {
                var a = document.getElementsByTagName('a');

                for (var i = a.length - 1; i >= 0; i--) {

                    if (options.addthis) {
                        trakk_addthis(a[i], window._gaq);
                    }

                    if (options.xing) {
                        trakk_xing(a[i], window._gaq);
                    }

                    if (options.custom) {
                        trakk_custom(a[i], window._gaq);
                    }
                }
            }
        });

    };




    /*
     * Enqueue Facebook tracking
     * The function is queued until the FB object is found and
     * the tracking can be bound to the  actions.
     * At this point three different social interaction of Facebook
     * are tracked: Likes, Unlikes and Shares
     * Tracking can be limited by using the options parameter
     *
     * @param   options     Tracking options for Facebook
     *                      Set to true tracks all social interactions or
     *                      Possible options: {
     *                          like: true,     // Track likes
     *                          unlike: true    // Track unlikes
     *                          share: true     // Track shares
     *                      }
     * @return  void
     */
    var trakk_facebook = function(options, embed) {

        if(embed){
            log('Embedding Facebook script');

            if(!document.getElementById('fb-root')){
                var root = document.createElement('div');
                    root.id = 'fb-root';
				document.getElementsByTagName('body')[0].appendChild(root);
            }

            embed_script('//connect.facebook.net/en_US/all.js#xfbml=1', 'fb-root');

        }

        log('Enqueueing Facebook tracking');

        enqueue(function() {

            try{ FB && FB.Event && FB.Event.subscribe && _gaq } catch(e) { return false; }

            log('Binding Facebook tracking');

            var intents = { 'like':     'edge.create',
                            'unlike':   'edge.remove',
                            'share':    'messages.send' };

            for (intent in intents) {

                if (options === true || options[intent] !== false) {

                    // Creating a scope around the subscription to avoid the infamous loop problem
                    FB.Event.subscribe(intents[intent], (function(intent) {

                        return function(targetUrl, obj){

                            if (options.fbref && obj && obj['_attr'] && obj['_attr'].ref) {
                                if (targetUrl.indexOf('?') === -1) {
                                   targetUrl += '?fbref=' + obj['_attr'].ref;
                                } else {
                                    targetUrl += '&fbref=' + obj['_attr'].ref;
                                }
                            }

                            log('Tracked Facebook interaction (' + intent + ') with ' + targetUrl);

                            _gaq.push(['_trackSocial', 'facebook', intent, targetUrl]);

                            if (options.fbrefevent === true) {
                                var fbref = extractParameter(window.location.href, "fb_ref");
                                if (fbref) {
                                    _gaq.push(['_trackEvent', 'TrakkSocial', 'fbref', fbref, null, true]);
                                }
                            }

                            if (options.fbsourceevent === true) {
                                var fbref = extractParameter(window.location.href, "fb_source");
                                if (fbref) {
                                    _gaq.push(['_trackEvent', 'TrakkSocial', 'fbsource', fbref, null, true]);
                                }
                            }

                        };
                    })(intent));
                    
                }
            }

            return true;
        });
    };



    /*
     * Enqueue Twitter tracking
     * The function is queued until the twttr object is found and
     * the tracking can be bound to the tweet action.

     * @param   options     Tracking options for Twitter
     *                      Set to true tracks all social interactions or
     *                      Possible options: {
     *                          tweet: true,    // Track tweets
     *                          retweet: true   // Track retweets
     *                          favorite: true  // Track favorite-ing
     *                          follow: true    // Track following
     *                      }
     *
     * @return  void
     */
    var trakk_twitter = function (options, embed) {

        if(embed){
            log('Embedding Twitter script');
            embed_script('//platform.twitter.com/widgets.js');
        }

        log('Enqueueing Twitter tracking');

        enqueue(function () {

            try{ twttr && twttr.events && twttr.events.bind && _gaq } catch(e) { return false; }

            log('Binding Twitter tracking');

            // Batch binding because bind() doesn't allow multiple intents, yay!
            var intents = ['tweet', 'retweet', 'favorite', 'follow'];

            for(var i = 0; i < intents.length; i++) {

                var intent = intents[i];

                if(options === true || options[intent] !== false) {
                    twttr.events.bind(intent, function(event) {
                        if(event) {
                            var targetUrl;

                            if (event.target && event.target.nodeName == 'IFRAME') {
                                targetUrl = extractParameter(event.target.src, 'url');
                            }

                            log('Tracked Twitter interaction (' + event.type + ')');

                            _gaq.push(['_trackSocial', 'twitter', event.type, targetUrl]);
                        }
                    });
                }
            }

            return true;
        });
    };



    /*
     * Enqueue LinkedIn share button tracking
     * The function is queued until the IN object is found.
     * As soon as that happens, the function looks for a script tag
     * with the in/share type that holds the data-success attribute
     * Then a function is set up that has the name of the data-success value
     * That's LinkedIn's idea of event listeners
     *
     * @return  void
     */
    var trakk_linkedin = function (options, embed) {

        if(embed){
            log('Embedding LinkedIn script');
            embed_script('//platform.linkedin.com/in.js');
        }

        log('Enqueueing LinkedIn tracking');

        enqueue(function () {

            try{ IN && _gaq } catch(e) { return false; }

            log('Binding LinkedIn tracking!');

            var s = document.getElementsByTagName('script');
            for (var i = 0; i < s.length; i++) {
                if (s[i].type.toLowerCase().indexOf('in/share') >= 0 && s[i].getAttribute('data-success')) {
                    window[ s[i].getAttribute('data-success') ] = function() {
                        log('Tracked LinkedIn interaction (share)');
                        _gaq.push(['_trackSocial', 'linkedin', 'share']);
                    };
                    break;
                }
            }

            return true;
        });
    };



    /*
     * Enqueue AddThis share button tracking
     * Xing share links are nothing but that: Simple <a/> tags with
     * a link to the Xing share endpoint URL.
     * Upon window.load all links are checked and if necessary the
     * tracking call is added to the onmousedown event of the links.
     *
     * @return  void
     */
    var trakk_addthis = function (a, gaq) {

        var url = a.getAttribute('addthis:url');

        if (url != null){

            log('Binding AddThis tracking');

            var action  = a.className.match(/addthis_button_([^\s\"]*)/);

            if (action && action.length >= 2) {

                action = action[1];

                addClickEvent(a, function (action, url) {
                    log('Tracked Addthis interaction (' + action + ')');
                    gaq.push(['_trackSocial', 'addthis', action, url]);
                });
            }
        }
    };



    /*
     * Enqueue Xing share button tracking
     * Xing share links are nothing but that: Simple <a/> tags with
     * a link to the Xing share endpoint URL.
     * Upon window.load all links are checked and if necessary the
     * tracking call is added to the onmousedown event of the links.
     *
     * @return  void
     */
    var trakk_xing = function (a, gaq) {

        if (window._gaq && a.href.match(/https:\/\/www\.xing\.com\/app\/user(.*?)op=share/i)) {

            log('Binding Xing tracking');

            addClickEvent(a,  function () {
                log('Tracked Xing interaction (share)');
                window._gaq.push(['_trackSocial', 'xing', 'share', extractParameter(a.href, 'url')]);
            });
        }
    };



    /*
     * Enqueue custom social tracking via data attribute
     *
     * @return  void
     */
    var trakk_custom = function (a, gaq) {

        if (window._gaq){

            data = a.getAttribute('data-trakksocial');

            if (data) {

              log('Binding Custom Social tracking');

                data = data.split(',', 3);

                if (data.length < 2) {
                    return;
                }

                addClickEvent(a, function () {
                    log('Tracked ' + data[0] + ' interaction (' + data[1] + ')');

                    data.unshift('_trackSocial');
                    if (data.length === 3) {
                        data.push(window.location.href);
                    }
                    window._gaq.push(data.slice(0, 4));
                });
            }
        }
    };



    /*
     * Sets up a callback function to be called repeatedly
     * until it returns true. This function is used by all other tracking
     * functions to allow them to wait until the object they are relying on
     * has been initialized
     *
     * @param   callback    The callback to be executed until returning true
     * @param   options     Options object, possible options:
     *                      limit       Number of tries, -1 for unlimited tries, defaults to 40
     *                      interval    Milliseconds between tries, defaults to 250 (= 10 seconds of trying)
     *                      success     Callback called once the main callback was successful
     *                      failure     Callback called once the limit was reached without
     *                                  the main callback being successful
     * @return  void
     */
    var enqueue = function(callback, options) {

        var options     = options || {};
        var limit       = options.limit || 40;
        var interval    = options.interval || 250;
        var counter     = 0;

        (function() {
            if (limit === -1 || limit > counter++) {
                if (false == callback()) {
                    window.setTimeout(arguments.callee, interval);
                } else if (options.success) {
                    options.success();
                }
            } else if (options.failure) {
                options.failure();
            }
        })();
    };



    /*
     * Helper function to embed a script element on window load
     * Inspired by http://null-uk.com/55090968
     *
     * @param   src     URL of the script file to embed
     * @return  void
     */
    var embed_script = function (src, appendTo) {
        addLoadEvent(function(){
            var e       = document.createElement('script');
                e.type  = 'text/javascript';
                e.async = true;
                e.src   = src;

            if (appendTo) {
                document.getElementById(appendTo).appendChild(e);
            } else {
                (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(e);
            }
        });
    };




    /*
     * Helper function to extract a specific query parameter from a URL.
     * Source: http://code.google.com/apis/analytics/docs/tracking/gaTrackingSocial.html#twitter
     *
     * @param   uri         The URL to extract the parameter from
     * @param   parameter   The name of the parameter to look for
     * @return  null or value of the parameter
     */
    var extractParameter = function (uri, parameter) {
        if (!uri) {
            return;
        }
        var uri     = uri.split('#')[0];
        var parts   = uri.split('?');

        if (parts.length == 1) {
            return false;
        }

        var query    = decodeURI(parts[1]).replace(';', '&');
        parameter   += '=';
        var params   = query.split('&');

        for (var i = 0, param; param = params[i]; ++i) {
            if (param.indexOf(parameter) === 0) {
                return unescape(param.split('=')[1]);
            }
        }

        return false;
    };



    /*
     * Helper function to add a function to the window onload event
     * Source: http://simonwillison.net/2004/May/26/addLoadEvent/
     *
     * @param   func            Function to attach to the window's onload event
     * @return  void
     */
    var addLoadEvent = function (func) {
        var oldonload = window.onload;
        if (typeof window.onload != 'function') {
            window.onload = func;
        } else {
            window.onload = function() {
                if (oldonload) {
                    oldonload();
                }
                func();
            }
        }
    };



    /*
     * Helper function to add a function to an element's onmousedown event
     * (Even though this is the addClickEvent method, it attaches to onmousedown,
     *  because it is more reliable.)
     *
     * @param   element     Element to attach the function to
     * @param   func        Function to attach to the element's onmousedown event
     * @return  void
     */
    var addClickEvent = function (element, func) {
        var oldonmousedown = element.onmousedown;
        if (typeof element.onmousedown != 'function') {
            element.onmousedown = func;
        } else {
            element.onmousedown = function () {
                if (oldonmousedown) {
                    oldonmousedown();
                }
                func();
            }
        }
    };




    var log = function (message) {
        if (debug && console && console.log) {
            console.log('TrakkSocial: ' + message);
        }
    };



    /*
     * Revealing pattern: Selected methods are presented to
     * the outside by returning them here
     */
    return {
        init: init
    }


})(window, document);


